package com.reformer.commons.httpclient;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;

import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;

/**
 * Http连接池管理器
 *
 * @author Administrator
 */
public class HttpConnectionManager {
    /**
     * 最大连接数
     */
    public final static int MAX_TOTAL = 2000;
    /**
     * 获取连接的最大等待时间
     */
    public final static int SOCKET_TIMEOUT = 5000;
    /**
     * 每个路由最大连接数
     */
    public final static int MAX_PER_ROUTE = 200;
    /**
     * 连接超时时间
     */
    public final static int CONNECT_TIMEOUT = 4000;
    /**
     * 读取超时时间
     */
    public final static int READ_TIMEOUT = 3000;

    private static PoolingHttpClientConnectionManager connManager;

    static {
        ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
        LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainsf).register("https", sslsf).build();
        connManager = new PoolingHttpClientConnectionManager(registry);
        // 将最大连接数增加到200
        connManager.setMaxTotal(MAX_TOTAL);
        // 将每个路由基础的连接增加到20
        connManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
        connManager.setMaxPerRoute(new HttpRoute(new HttpHost("192.168.10.60", 8080)), 400);
        // 将目标主机的最大连接数增加到50
        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(SOCKET_TIMEOUT).build();
        connManager.setDefaultSocketConfig(socketConfig);
    }

    private HttpConnectionManager() {
    }

    /**
     * 获取连接池
     *
     * @return
     */
    public static CloseableHttpClient getConnection() {
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(READ_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
        CloseableHttpClient httpClient = HttpClients
                .custom()//
                .setConnectionManager(connManager)//
                .setRetryHandler(new HttpRequestRetryHandler() {
                    @Override
                    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                        if (executionCount >= 5) {
                            // 如果超过最大重试次数，那么就不要继续了
                            return false;
                        }
                        if (exception instanceof NoHttpResponseException) {
                            // 如果服务器丢掉了连接，那么就重试
                            return true;
                        } else if (exception instanceof SSLHandshakeException) {
                            // 不要重试SSL握手异常
                            return false;
                        }
                        HttpRequest request = (HttpRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST);
                        boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
                        if (idempotent) {
                            // 如果请求被认为是幂等的，那么就重试
                            return true;
                        }
                        return false;
                    }
                })//
                .setDefaultRequestConfig(requestConfig)//
                .build();
        if (connManager != null && connManager.getTotalStats() != null) {
            System.out.println("now client pool " + connManager.getTotalStats().toString());
        }
        return httpClient;
    }
}
